﻿namespace NJQuery {
	using System;
	using System.Collections;
	using System.Collections.Generic;
	using System.ComponentModel;
	using System.Globalization;
	using System.Text;

	/// <summary>
	/// Provides a specialized collection of localized day of the week names.
	/// </summary>
	[Serializable, TypeConverter(typeof(DayNameCollectionConverter))]
	public class DayNameCollection : IEnumerable<string> {


		#region Constants

		private const int dayCount = 7;

		#endregion


		#region Fields

		private Culture culture;
		private string[] customNames;

		#endregion


		#region Constructors

		/// <summary>
		/// Initializes a new instance of the <see cref="DayNameCollection"/> class.
		/// </summary>
		/// <param name="length">The <see cref="DayNameLength"/> value representing the standard client-side jQuery UI day of the week name length.</param>
		public DayNameCollection(DayNameLength length) {
			customNames = new string[dayCount];
			Length = length;
		}

		#endregion


		#region Indexers

		/// <summary>
		/// Returns the localized day of the week using a zero-based index.
		/// </summary>
		/// <param name="dayOfWeek">Zero-based index for the day of week (0 represents Sunday).</param>
		/// <returns>The localized day of the week.</returns>
		public string this[DayOfWeek dayOfWeek] {
			get { return GetName(dayOfWeek); }
			set { SetName(dayOfWeek, value); }
		}

		#endregion


		#region Properties: Public

		/// <summary>
		/// Gets the number of items in the collection.
		/// </summary>
		[Browsable(false)]
		public DayNameLength Length { get; private set; }

		/// <summary>
		/// Gets or sets the culture associated with the collection.
		/// </summary>
		[DefaultValue(Culture.Invariant), RefreshProperties(RefreshProperties.Repaint), ParenthesizePropertyName(true)]
		public Culture Culture {
			get { return culture; }
			set { culture = value; }
		}

		/// <summary>
		/// Gets or sets the localized name for Sunday.
		/// </summary>
		[RefreshProperties(RefreshProperties.Repaint)]
		public string Sunday {
			get { return GetName(DayOfWeek.Sunday); }
			set { SetName(DayOfWeek.Sunday, value); }
		}

		/// <summary>
		/// Gets or sets the localized name for Monday.
		/// </summary>
		[RefreshProperties(RefreshProperties.Repaint)]
		public string Monday {
			get { return GetName(DayOfWeek.Monday); }
			set { SetName(DayOfWeek.Monday, value); }
		}

		/// <summary>
		/// Gets or sets the localized name for Tuesday.
		/// </summary>
		[RefreshProperties(RefreshProperties.Repaint)]
		public string Tuesday {
			get { return GetName(DayOfWeek.Tuesday); }
			set { SetName(DayOfWeek.Tuesday, value); }
		}

		/// <summary>
		/// Gets or sets the localized name for Wednesday.
		/// </summary>
		[RefreshProperties(RefreshProperties.Repaint)]
		public string Wednesday {
			get { return GetName(DayOfWeek.Wednesday); }
			set { SetName(DayOfWeek.Wednesday, value); }
		}

		/// <summary>
		/// Gets or sets the localized name for Thursday.
		/// </summary>
		[RefreshProperties(RefreshProperties.Repaint)]
		public string Thursday {
			get { return GetName(DayOfWeek.Thursday); }
			set { SetName(DayOfWeek.Thursday, value); }
		}

		/// <summary>
		/// Gets or sets the localized name for Friday.
		/// </summary>
		[RefreshProperties(RefreshProperties.Repaint)]
		public string Friday {
			get { return GetName(DayOfWeek.Friday); }
			set { SetName(DayOfWeek.Friday, value); }
		}

		/// <summary>
		/// Gets or sets the localized name for Saturday.
		/// </summary>
		[RefreshProperties(RefreshProperties.Repaint)]
		public string Saturday {
			get { return GetName(DayOfWeek.Saturday); }
			set { SetName(DayOfWeek.Saturday, value); }
		}

		#endregion


		#region Properties: Protected

		/// <summary>
		/// Gets a <see cref="DateTimeFormatInfo"/> object defining how <see cref="DateTime"/> values are rendered for the specified <see cref="Culture"/> value.
		/// </summary>
		[Browsable(false)]
		protected virtual DateTimeFormatInfo Format {
			get {
				switch(Culture) {
					case Culture.Invariant: return DateTimeFormatInfo.InvariantInfo;
					case Culture.Current: return DateTimeFormatInfo.CurrentInfo;
					default: return null;
				}
			}
		}

		#endregion


		#region Methods: Public

		/// <summary>
		/// Returns an enumerator that iterates through a collection. 
		/// </summary>
		/// <returns>A <see cref="IEnumerator"/> object that can be used to iterate through the collection.</returns>
		public IEnumerator<string> GetEnumerator() {
			yield return Sunday;
			yield return Monday;
			yield return Tuesday;
			yield return Wednesday;
			yield return Thursday;
			yield return Friday;
			yield return Saturday;
		}

		/// <summary>
		/// Returns a <see cref="String"/> that represents the current <see cref="DayNameCollection"/> value.
		/// </summary>
		/// <returns>The string representation.</returns>
		public override string ToString() {
			if(Culture == Culture.Custom)
				return string.Format(CultureInfo.InvariantCulture, "(Custom: {0}, {1}, {2}, {3}, {4}, {5}, {6})",
					Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday);

			return string.Format(CultureInfo.InvariantCulture, "({0})", culture.ToString());
		}

		/// <summary>
		/// Converts the collection to a string array.
		/// </summary>
		/// <returns>A string array of values.</returns>
		public string[] ToArray() {
			string[] result = new string[dayCount];
			for(int index = 0; index < result.Length; index++)
				result[index] = GetName((DayOfWeek)index);
			return result;
		}

		#endregion


		#region Methods: Protected

		/// <summary>
		/// Gets the localized name for the provided day of the week.
		/// </summary>
		/// <param name="dayOfWeek">A <see cref="DayOfWeek"/> value representing the day of the week.</param>
		/// <returns>The localized day of the week name.</returns>
		protected virtual string GetName(DayOfWeek dayOfWeek) {
			DateTimeFormatInfo format = Format;
			if(format == null)
				return customNames[(int)dayOfWeek] ?? GetNameFromFormat(DateTimeFormatInfo.InvariantInfo, dayOfWeek);
			return GetNameFromFormat(format, dayOfWeek);
		}

		/// <summary>
		/// Sets the localized name for the provided day of the week.
		/// </summary>
		/// <param name="dayOfWeek">A <see cref="DayOfWeek"/> value representing the day of the week.</param>
		/// <param name="value">The localized day of the week name.</param>
		protected virtual void SetName(DayOfWeek dayOfWeek, string value) {
			string oldName = GetName(dayOfWeek);
			if(oldName != value) {
				if(string.IsNullOrEmpty(value) || (value == GetNameFromFormat(DateTimeFormatInfo.InvariantInfo, dayOfWeek)))
					customNames[(int)dayOfWeek] = null;
				else
					customNames[(int)dayOfWeek] = value;
			}
			Culture = Culture.Custom;
		}

		/// <summary>
		/// Looks up the standard day of the week name from the provided <see cref="DateTimeFormatInfo"/> object.
		/// </summary>
		/// <param name="format">A <see cref="DateTimeFormatInfo"/> object containing format information for <see cref="DateTime"/> objects.</param>
		/// <param name="dayOfWeek">A <see cref="DayOfWeek"/> value representing the day of the week.</param>
		/// <returns>The localized day of week name.</returns>
		protected virtual string GetNameFromFormat(DateTimeFormatInfo format, DayOfWeek dayOfWeek) {
			switch(Length) {
				case DayNameLength.Short: return format.GetAbbreviatedDayName(dayOfWeek);
				case DayNameLength.Minimum: return format.GetShortestDayName(dayOfWeek);
				default: return format.GetDayName(dayOfWeek);
			}
		}

		#endregion


		#region Methods: Serialization

		private void Reset(DayOfWeek dayOfWeek) {
			customNames[(int)dayOfWeek] = null;
		}

		private bool ShouldSerialize(DayOfWeek dayOfWeek) {
			if(Culture != Culture.Custom)
				return false;
			return (customNames[(int)dayOfWeek] != null);
		}

		private bool ShouldSerializeSunday() {
			return ShouldSerialize(DayOfWeek.Sunday);
		}
		private bool ShouldSerializeMonday() {
			return ShouldSerialize(DayOfWeek.Monday);
		}
		private bool ShouldSerializeTuesday() {
			return ShouldSerialize(DayOfWeek.Tuesday);
		}
		private bool ShouldSerializeWednesday() {
			return ShouldSerialize(DayOfWeek.Wednesday);
		}
		private bool ShouldSerializeThursday() {
			return ShouldSerialize(DayOfWeek.Thursday);
		}
		private bool ShouldSerializeFriday() {
			return ShouldSerialize(DayOfWeek.Friday);
		}
		private bool ShouldSerializeSaturday() {
			return ShouldSerialize(DayOfWeek.Saturday);
		}

		private void ResetSunday() {
			Reset(DayOfWeek.Sunday);
		}
		private void ResetMonday() {
			Reset(DayOfWeek.Monday);
		}
		private void ResetTuesday() {
			Reset(DayOfWeek.Tuesday);
		}
		private void ResetWednesday() {
			Reset(DayOfWeek.Wednesday);
		}
		private void ResetThursday() {
			Reset(DayOfWeek.Thursday);
		}
		private void ResetFriday() {
			Reset(DayOfWeek.Friday);
		}
		private void ResetSaturday() {
			Reset(DayOfWeek.Saturday);
		}

		#endregion


		#region Explicit implementation: IEnumerable

		IEnumerator IEnumerable.GetEnumerator() {
			return GetEnumerator();
		}

		#endregion


	}

}